package com.socket.server.utils;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @program: qingcheng
 * @author: XIONG CHUAN
 * @create: 2019-04-28 15:21
 * @description: netty服务端处理类
 **/


public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    private Logger log = LogManager.getLogger(getClass());

    private Object lock = new Object();

    private static volatile ConcurrentHashMap<String, String> deviceStatus = new ConcurrentHashMap<>();

    //创建一个消息队列
    public static LinkedBlockingQueue<String> reportFreqs = new LinkedBlockingQueue<String>(1000);

    @Resource
    private PeriodicDuty periodicDuty;
    /**
     * 管理一个全局map，保存连接进服务端的通道数量
     */
    private static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> CHANNEL_MAP = new ConcurrentHashMap<>();
    //创建一个绑定deviceID和channelId的map
    public static ConcurrentHashMap<String, ChannelId> deviceMap = new ConcurrentHashMap<>();
    //创建一个设备对应扫台结果的map
    public static volatile ConcurrentHashMap<String, String> deviceScanfreqs = new ConcurrentHashMap<>();
    //创建一个对应的设备队列
    public static LinkedBlockingQueue<String> deviceQueue = new LinkedBlockingQueue<String>(100);

    String[] white = {"879", "887", "899", "909", "914", "934", "940", "947", "955", "959", "962", "972", "977", "981",
            "990", "1001", "1003", "1009", "1017", "1025", "1027", "1037", "1051", "1057", "1065", "1067", "1072",
            "1077"};


    /**
     * @param ctx
     * @author xiongchuan on 2019/4/28 16:10
     * @DESCRIPTION: 有客户端连接服务器会触发此函数
     * @return: void
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {

        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();
        int clientPort = insocket.getPort();

        //获取连接通道唯一标识
        ChannelId channelId = ctx.channel().id();

        //如果map中不包含此连接，就保存连接
        if (CHANNEL_MAP.containsKey(channelId)) {
            log.info("客户端【" + channelId + "】是连接状态，连接通道数量: " + CHANNEL_MAP.size());
        } else {
            //保存连接
            CHANNEL_MAP.put(channelId, ctx);
            log.info("客户端【" + channelId + "】连接netty服务器[IP:" + clientIp + "--->PORT:" + clientPort + "]");
            log.info("连接通道数量: " + CHANNEL_MAP.size());
//            new Thread(new PeriodicDuty(lock,channelId)).start();
        }

    }

    /**
     * @param ctx
     * @author xiongchuan on 2019/4/28 16:10
     * @DESCRIPTION: 有客户端终止连接服务器会触发此函数
     * @return: void
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {

        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();

        ChannelId channelId = ctx.channel().id();

        //包含此客户端才去删除
        if (CHANNEL_MAP.containsKey(channelId)) {
            //删除连接
            CHANNEL_MAP.remove(channelId);
            //删除deviceMap中失效的连接
            Set<Map.Entry<String, ChannelId>> entries = deviceMap.entrySet();
            Iterator<Map.Entry<String, ChannelId>> iterator = entries.iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, ChannelId> next = iterator.next();
                if (next.getValue().equals(channelId)) {
                    deviceMap.remove(next.getKey());
                }
            }
            log.info("客户端【" + channelId + "】退出netty服务器[IP:" + clientIp + "--->PORT:" + insocket.getPort() + "]");
            log.info("连接通道数量: " + CHANNEL_MAP.size());

        }
    }

    /**
     * 扫台上报的数据{123456780001523@scanfreqs@
     * 879#899#909#914#934#940#947#955#962#972#977#981#990#1017#1028#1037#1052#1057#1072#1077@0.0#0.0}
     *
     * @param ctx
     * @author xiongchuan on 2019/4/28 16:10
     * @DESCRIPTION: 有客户端发消息会触发此函数
     * @return: void
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        String[] split = msg.toString().split("@");
        log.info("加载客户端报文......");
        log.info("【" + ctx.channel().id() + "】" + " :" + msg);
        String regex = "1[0-9]{14}";
        if (!split[0].matches(regex) || split.length < 2) {
            log.info("终端数据上报异常！！！");
            return;
        }
        deviceMap.put(split[0], ctx.channel().id());
        log.info("将设备信息和通道保存！！");
//        if (split[1].equals("reportstatus") && split[2].equals("0")) {
//        }
        if (split[1].equals("connect")) {
            if (deviceStatus.get(split[0]) == null) {
                deviceStatus.put(split[0], "true");
                log.info("设备状态已写入" + deviceStatus.get(split[0]));
                new Thread(new PeriodicDuty(lock, split[0])).start();
            } else {
                log.info("设备重新连接！！！");
                deviceStatus.put(split[0], "true");
                new Thread(new MessageUtil(lock, msg.toString())).start();
            }
        }
        if (split[1].equals("scanfreqs")) {
            if (split.length < 4 || !split[0].matches(regex)) {
                log.info("终端数据上报异常！！！");
                return;
            }
            deviceQueue.add(split[0]);
            log.info("接收到终端的扫台结果！！！");
//            reportFreqs.add(split[0] + "@" + split[2]);
            deviceScanfreqs.put(split[0], split[2]);
            log.info(deviceScanfreqs.toString() + "*************************");
//            new Thread(new MessageUtil(lock, msg.toString())).start();
        }
        if (split[1].equals("tune")) {
            log.info("我已经接收到终端调频完毕的消息！！！");
//            messageUtil.dealMessage(msg.toString());
//            List<String> whiteList = Arrays.asList(white);
//            //判断该频率是否为白名单；
//            if (whiteList.contains(split[2])) {
//                //判断执行周期，白名单每6个周期执行一次
//                if (periodicDuty.getCycle().get(split[0]) % 6 == 0) {
//                    String time = "30";
//                    String tuneMessage = "recording#" + split[2] + "#" + time;
//
//                    log.info("向设备" + split[0] + "：" + deviceMap.get(split[0]) + "发送录音任务！！！！");
//                    try {
//                        channelWrite(deviceMap.get(split[0]), tuneMessage);
//                    } catch (Exception e) {
//                        e.printStackTrace();
//                    }
//                } else {
//                    return;
//                }
//            }
//            String time = "30";
//            String tuneMessage = "recording#" + split[2] + "#" + time;
//
//            log.info("向设备" + split[0] + "：" + deviceMap.get(split[0]) + "发送录音任务！！！！");
//            try {
//                channelWrite(deviceMap.get(split[0]), tuneMessage);
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
        }
        if (split[1].equals("recording")) {
//            messageUtil.dealMessage(msg.toString());
//            new Thread(new MessageUtil(lock, msg.toString())).start();
            log.info("我已经接收到终端录音完毕消息！！！！");
        }
        log.info("【" + ctx.channel().id() + "】" + " :" + deviceMap);
//        if (split[2].equals("scanfreqs")){
//            deviceMap.put(split[0],ctx.channel().id());
//            messageUtil.dealMessage(msg.toString());
//        }
        /**
         *  下面可以解析数据，保存数据，生成返回报文，将需要返回报文写入write函数
         *
         */
        //响应客户端
//        this.channelWrite(ctx.channel().id(), msg);
    }

    /**
     * @param msg       需要发送的消息内容
     * @param channelId 连接通道唯一id
     * @author xiongchuan on 2019/4/28 16:10
     * @DESCRIPTION: 服务端给客户端发送消息
     * @return: void
     */
    public void channelWrite(ChannelId channelId, Object msg)  {

        ChannelHandlerContext ctx = CHANNEL_MAP.get(channelId);

        if (ctx == null) {
            log.info("通道【" + channelId + "】不存在");

            return;
        }

        if (msg == null) {
            log.info("服务端响应空的消息");
            return;
        }
        ;
        log.info("向" + channelId + "下发" + msg);
        //将客户端的信息直接返回写入ctx
        ctx.write(msg);
        //刷新缓存区
        ctx.flush();

    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

        String socketString = ctx.channel().remoteAddress().toString();

        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                log.info("Client: " + socketString + " READER_IDLE 读超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                log.info("Client: " + socketString + " WRITER_IDLE 写超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.ALL_IDLE) {
                log.info("Client: " + socketString + " ALL_IDLE 总超时");
                ctx.disconnect();
            }
        }
    }

    /**
     * @param ctx
     * @author xiongchuan on 2019/4/28 16:10
     * @DESCRIPTION: 发生异常会触发此函数
     * @return: void
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {


        ctx.close();
        log.info(ctx.channel().id() + " 发生了错误,此连接被关闭" + "此时连通数量: " + CHANNEL_MAP.size());
        //cause.printStackTrace();
    }







    public static ConcurrentHashMap<ChannelId, ChannelHandlerContext> getChannelMap() {
        return CHANNEL_MAP;
    }

    public static ConcurrentHashMap<String, String> getDeviceStatus() {
        return deviceStatus;
    }

    public static void setDeviceStatus(ConcurrentHashMap<String, String> deviceStatus) {
        NettyServerHandler.deviceStatus = deviceStatus;
    }
}
